
package com.tsystems.school.jabber.server;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;

import com.tsystems.school.jabber.Message;
import com.tsystems.school.jabber.MessageAttrs;
import com.tsystems.school.jabber.MessageType;
import com.tsystems.school.jabber.ParsingType;
import com.tsystems.school.jabber.server.workers.LOGINWorker;
import com.tsystems.school.jabber.server.workers.WorkerFactory;

public class IOWorker implements Runnable {
  
  private static final int LifeTime = 1000;
  private Date             starttime;
  
  public ConnectedUser getConnUser() {
    return connUser;
  }

  
  public void setConnUser(ConnectedUser connUser) {
    this.connUser = connUser;
  }

  private ConnectedUser    connUser;
  private SocketChannel    socketchannel;
  private ByteBuffer       buffer   = ByteBuffer.allocate(4000);
  
  private void writeMessage(Message msg) {
    buffer.clear();
    String t = msg.toJSON();
    t = t.length() + t;
    buffer.put(t.getBytes());
    try {
      socketchannel.write(buffer);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  public IOWorker(SocketChannel channel) {
    this.socketchannel = channel;
  }
  
  public IOWorker(SocketChannel channel, ConnectedUser user) {
    this.socketchannel = channel;
    this.connUser = user;
  }
  
  public IOWorker(ConnectedUser user) {
    this.socketchannel = user.getChanel();
    this.connUser = user;
  }
  
  @Override
  public void run() {
    starttime = new Date();
    // TODO Auto-generated method stub
    Selector sel;
    ByteBuffer b = ByteBuffer.allocate(2);
    boolean read = false;
    boolean done = false;
    try {
      String response = "";
      sel = Selector.open();
      
      try {
        socketchannel.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        while (!done) {
          sel.select();
          Iterator<SelectionKey> it = sel.selectedKeys().iterator();
          while (it.hasNext()) {
            SelectionKey key = (SelectionKey) it.next();
            it.remove();
            buffer.clear();
            if (key.isReadable()) {
              while (socketchannel.read(buffer) > 0) {
                read = true;
                String encoding = System.getProperty("file.encoding");
                CharBuffer cb = (Charset.forName(encoding)).decode((ByteBuffer) buffer.flip());
                response = response + cb.toString();
              }
              while (response.length() > 0) {
                int size = Integer.parseInt(response.substring(0, response.indexOf("{")));
                String m = response.substring(response.indexOf("{"), response.indexOf("{") + size);
                response = response.substring(response.indexOf("{") + size);
                Message msg = Message.LoadFromJSON(m);
                if (connUser == null) {
                  if ((msg.getType() != MessageType.LOGIN) && (msg.getType() != MessageType.NEWUSER)) {
                    return;
                  } else {
                    if (msg.getType() == MessageType.NEWUSER){
                      writeMessage((new WorkerFactory(msg)).work());
                      return;
                    }
                    if (msg.getType() == MessageType.LOGIN){
                      LOGINWorker w = new LOGINWorker(msg, socketchannel, this);
                      Message out = w.work(msg);
                      if (out.getType() == MessageType.CANCEL){
                        writeMessage(out);
                        return;
                      }
                    }
                  }
                } else {
                  (new Thread(new WorkerFactory(msg))).start();
                  if (msg.getType().getPt() == ParsingType.EASY){
                    Message out = new Message();
                    out.setType(MessageType.OK);
                    out.putProperty(MessageAttrs.RES.getAttrname(), "1");
                    writeMessage(out);
                  }
                }
                starttime = new Date();
              }
            }
            //&& read
            if (key.isWritable()) {   
              if (connUser != null){
                Message min = null;
                while (( min = (Message)(connUser.getWriteMessagePool().get()))!= null){
                  writeMessage(min);
                  starttime = new Date();
                }
              }
            }
          }
          Calendar cal = Calendar.getInstance();
          cal.setTime(starttime);
          cal.add(Calendar.SECOND, LifeTime);
          if ((new Date()).after(cal.getTime())){
            done = true;
            return;
          }
            
        }
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        try {
          sel.close();
          if (connUser != null) {
            connUser.setChanel(null);
            connUser.setIOWorker(null);
          }
          connUser = null;
          socketchannel.close();
          return;
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
